package io.kimmking.dubbo.common.distributelock;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

/**
 * 使用Aspect对自定义的分布式锁注解@dlock进行aop处理
 */
@Slf4j
@Aspect
public class DlockAspect {
    @Autowired(required = false)
    private DistributedLock dLocker;

    /**
     * SpEL表达式解析
     */
    private static final SpelExpressionParser spelExpressionParser;
    /**
     * 用于获取方法入参的参数名
     */
    private static final DefaultParameterNameDiscoverer nameDiscoverer;

    static {
        spelExpressionParser = new SpelExpressionParser();
        nameDiscoverer = new DefaultParameterNameDiscoverer();
    }

    @Around("@within(dlock) || @annotation(dlock)")
    public Object aroundDLock(ProceedingJoinPoint point, dlock lock) throws Throwable{
        if (ObjectUtil.isNull(dLocker))
            throw new RuntimeException("DistributedLock is null!");

        if (ObjectUtil.isNull(lock)) {
            lock = point.getTarget().getClass().getDeclaredAnnotation(dlock.class);
        }

        String lockKey = lock.key();
        if (StrUtil.isEmpty(lockKey))
            throw new RuntimeException("lockKey is null!");

        //对lockKey进行SpEL表达式解析(如果是SpEL表达式)
        if (lockKey.startsWith("#")) {
            MethodSignature methodSignature = (MethodSignature) point.getSignature();
            //获取签名方法的入参值
            Object[] args = point.getArgs();
            //解析出具体的lockKey的值
            lockKey = getValBySpEL(lockKey, methodSignature, args);
        }

        ZLock zLock = null;
        try {
            if (lock.waitTime() > 0) {
                zLock = dLocker.tryLock(lockKey, lock.waitTime(), lock.leaseTime(), lock.unit(), lock.isFair());
            } else {
                zLock = dLocker.lock(lockKey, lock.leaseTime(), lock.unit(), lock.isFair());
            }

            if (!ObjectUtil.isNull(zLock)) {
                return point.proceed();
            } else {
                throw new RuntimeException("Execute lock failed for " + lockKey);
            }
        } finally {
            dLocker.unlock(zLock);
        }

    }

    /**
     * 解析spEL表达式
     */
    private String getValBySpEL(String spEL, MethodSignature methodSignature, Object[] args) {
        //获取方法形参名数组
        String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod());
        if (paramNames != null && paramNames.length > 0) {
            Expression expression = spelExpressionParser.parseExpression(spEL);
            // spring的表达式上下文对象
            EvaluationContext context = new StandardEvaluationContext();
            // 给上下文赋值
            for(int i = 0; i < args.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
            //解析出spEL表达式最终的具体值
            return expression.getValue(context, String.class);
        }
        return null;
    }

}
